﻿@using FluentValidation

<MudCard>
    <MudForm Model="@_model" @ref="@_form" Validation="@(_orderValidator.ValidateValue)" ValidationDelay="0">
        <MudCardContent>
            <MudTextField @bind-Value="_model.Name"
                          For="@(() => _model.Name)"
                          Immediate="true"
                          Label="Name" />

            <MudTextField @bind-Value="_model.Email"
                          For="@(() => _model.Email)"
                          Immediate="true"
                          Label="Email" />

            <MudTextField @bind-Value="_model.CCNumber"
                          For="@(() => _model.CCNumber)"
                          Immediate="true"
                          Label="Credit card nr" />

            <MudTextField @bind-Value="_model.Address.Address"
                          For="@(() => _model.Address.Address)"
                          Immediate="true"
                          Label="Address" />

            <MudTextField @bind-Value="_model.Address.City"
                          For="@(() => _model.Address.City)"
                          Immediate="true"
                          Label="City" />

            <MudTextField @bind-Value="_model.Address.Country"
                          For="@(() => _model.Address.Country)"
                          Immediate="true"
                          Label="Country" />
        </MudCardContent>
        <MudCardContent Class="pa-0">

            <MudTable Items="@_model.OrderDetails" Hover="true" Breakpoint="Breakpoint.None" Dense="@true" Elevation="0">
                <HeaderContent>
                    <MudTh>Description</MudTh>
                    <MudTh>Offer</MudTh>
                </HeaderContent>
                <RowTemplate>
                    <MudTd DataLabel="Description">
                        <MudForm Model="@context" Validation="@(_orderDetailsValidator.ValidateValue)">
                            <MudTextField Label="Enter Description"
                                          @bind-Value="context.Description"
                                          For="(() => context.Description)" />
                        </MudForm>
                    </MudTd>
                    <MudTd DataLabel="Offer">
                        <MudForm Model="@context">
                            <MudNumericField Label="Enter Offer"
                                             @bind-Value="context.Offer"
                                             Validation="@(_orderDetailsValidator.ValidateValue)"
                                             For="(() => context.Offer)" />
                        </MudForm>
                    </MudTd>
                </RowTemplate>
            </MudTable>

        </MudCardContent>
    </MudForm>
    <MudCardActions>
        <MudButton Variant="Variant.Filled" Color="Color.Primary" Class="ml-auto" OnClick="@(async () => await Submit())">Order</MudButton>
    </MudCardActions>
</MudCard>

@code {
    [Inject]
    private ISnackbar Snackbar { get; set; } = null!;

    private MudForm _form = null!;

    private readonly OrderModelFluentValidator _orderValidator = new();

    private readonly OrderDetailsModelFluentValidator _orderDetailsValidator = new();

    private readonly OrderModel _model = new OrderModel();

    public class OrderModel
    {
        public string? Name { get; set; }

        public string? Email { get; set; }

        public string CCNumber { get; set; } = "4012 8888 8888 1881";

        public AddressModel Address { get; set; } = new();

        public List<OrderDetailsModel> OrderDetails =
        [
            new()
            {
                Description = "Perform Work order 1",
                Offer = 100
            },

            new()
        ];
    }

    public class AddressModel
    {
        public string? Address { get; set; }

        public string? City { get; set; }

        public string? Country { get; set; }
    }

    public class OrderDetailsModel
    {
        public string? Description { get; set; }

        public decimal Offer { get; set; }
    }

    private async Task Submit()
    {
        await _form.Validate();

        if (_form.IsValid)
        {
            Snackbar.Add("Submitted!");
        }
    }

    /// <summary>
    /// A standard AbstractValidator which contains multiple rules and can be shared with the back end API
    /// </summary>
    public class OrderModelFluentValidator : AbstractValidator<OrderModel>
    {
        public OrderModelFluentValidator()
        {
            RuleFor(x => x.Name)
                .NotEmpty()
                .Length(1, 100);

            RuleFor(x => x.Email)
                .Cascade(CascadeMode.Stop)
                .NotEmpty()
                .EmailAddress()
                .MustAsync(async (value, cancellationToken) => await IsUniqueAsync(value));

            RuleFor(x => x.CCNumber)
                .NotEmpty()
                .Length(1, 100)
                .CreditCard();

            RuleFor(x => x.Address.Address)
                .NotEmpty()
                .Length(1, 100);

            RuleFor(x => x.Address.City)
                .NotEmpty()
                .Length(1, 100);

            RuleFor(x => x.Address.Country)
                .NotEmpty()
                .Length(1, 100);

            RuleForEach(x => x.OrderDetails)
                .SetValidator(new OrderDetailsModelFluentValidator());
        }

        private async Task<bool> IsUniqueAsync(string email)
        {
            // Simulates a long running http call
            await Task.Delay(2000);
            return email.ToLower() != "test@test.com";
        }

        public Func<object, string, Task<IEnumerable<string>>> ValidateValue => async (model, propertyName) =>
        {
            var result = await ValidateAsync(ValidationContext<OrderModel>.CreateWithOptions((OrderModel)model, x => x.IncludeProperties(propertyName)));
            if (result.IsValid)
                return Array.Empty<string>();
            return result.Errors.Select(e => e.ErrorMessage);
        };
    }

    /// <summary>
    /// A standard AbstractValidator for the Collection Object
    /// </summary>
    public class OrderDetailsModelFluentValidator : AbstractValidator<OrderDetailsModel>
    {
        public OrderDetailsModelFluentValidator()
        {
            RuleFor(x => x.Description)
                .NotEmpty()
                .Length(1, 100);

            RuleFor(x => x.Offer)
                .GreaterThan(0)
                .LessThan(999);
        }

        public Func<object, string, Task<IEnumerable<string>>> ValidateValue => async (model, propertyName) =>
        {
            var result = await ValidateAsync(ValidationContext<OrderDetailsModel>.CreateWithOptions((OrderDetailsModel)model, x => x.IncludeProperties(propertyName)));
            if (result.IsValid)
                return Array.Empty<string>();
            return result.Errors.Select(e => e.ErrorMessage);
        };
    }
}